package repl

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"showen7/eval"
	"showen7/inter"
	"showen7/lex"
	"showen7/parse"
	"showen7/tok"
	"strings"
)

const PROMPT = ">>> "
const CONTINUATION_PROMPT = "... "

func StartTerminal(in *bufio.Reader, out *bufio.Writer) {
	globalEnv := inter.NewEnvironment()

	eval.RegisterBuiltinProcedures(globalEnv)
	//eval.LoadLibrary(globalEnv)

	printWelcomeMessage(out)
	flushWriter(out)
	for {
		fmt.Fprintf(out, PROMPT)
		flushWriter(out)

		var input strings.Builder
		for {
			line, err := in.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					if len(strings.TrimSpace(line)) > 0 {
						input.WriteString(line)
						evalExpression(input.String(), out, globalEnv)
					}
					flushWriter(out)
					return
				}
				fmt.Fprintf(out, "Error reading input: %s\n", err)
				flushWriter(out)
				return
			}

			// Add the current line to input
			input.WriteString(line)

			// Check if more input is needed (whether parentheses match)
			if needsMoreInput(input.String()) {
				// If in interactive mode, display continuation prompt
				fmt.Fprintf(out, CONTINUATION_PROMPT)
				flushWriter(out)
				continue
			}

			// Parentheses matching completed, break loop
			break
		}

		// Handle exit command (only in interactive mode)
		trimmed := strings.TrimSpace(input.String())
		if trimmed == "(exit)" || trimmed == "(quit)" {
			flushWriter(out)
			return
		}
		// Handle empty input
		if strings.TrimSpace(input.String()) == "" {
			continue
		}

		// Execute expression
		evalExpression(input.String(), out, globalEnv)

	}
}

// needsMoreInput checks if the input needs more lines to complete
// by checking if all parentheses are balanced and strings are closed
func needsMoreInput(input string) bool {
	// Create a lexer to tokenize the input
	l := lex.NewLexer(input)

	// Count parentheses
	leftParenCount := 0
	rightParenCount := 0

	for {
		token := l.NextToken()
		if token.Type == tok.EOF {
			break
		}

		switch token.Type {
		case tok.LPAREN:
			leftParenCount++
		case tok.RPAREN:
			rightParenCount++
		case tok.ILLEGAL:
			// Check if this is an unclosed string
			if len(token.Lexeme) > 0 && token.Lexeme[0] == '"' {
				// This looks like the start of a string but it's not properly closed
				return true
			}
		}
	}

	// If we have more left parentheses than right ones, we need more input
	return leftParenCount > rightParenCount
}

// isInteractive checks if the input is coming from a terminal

// printWelcomeMessage prints welcome message
func printWelcomeMessage(out *bufio.Writer) {
	fmt.Fprintln(out, "Welcome to Showen!")
	fmt.Fprintln(out, "Type an expression to evaluate, or (exit) to quit.")
}

// flushWriter flushes the writer to ensure output is displayed
func flushWriter(out *bufio.Writer) {
	if err := out.Flush(); err != nil {
		fmt.Fprintf(os.Stderr, "Error flushing output: %s\n", err)
	}
}

// evalExpression evaluates expression and outputs result
func evalExpression(input string, out *bufio.Writer, env *inter.Environment) {
	// Create lexer
	l := lex.NewLexer(input)
	tokens := l.ReadTokens()
	correct := true
	for _, token := range tokens {
		if token.Type == tok.ILLEGAL {
			fmt.Fprintf(out, "Syntax Error: %s in %v:%v\n", token.Lexeme, token.Line, token.Column)
			flushWriter(out)
			correct = false
		}
	}
	if !correct {
		return
	}

	// Create parser
	p := parse.NewParser(tokens)

	// Parse expression
	exprs, err := p.ParseProgram()
	if err != nil {
		fmt.Fprintf(out, "Parse error: %s\n", err)
		flushWriter(out)
		return
	}

	for _, expr := range exprs {
		if expr == nil {
			return
		}
		result, err := eval.EvalExpression(expr, env)
		if err != nil {
			fmt.Fprintf(out, "Eval error: %s\n", err)
			flushWriter(out)
			return
		}

		printResult(result, out)
	}
}

// printResult prints the evaluation result of expression
func printResult(result inter.Object, out *bufio.Writer) {
	if result == inter.NULL_OBJECT {
		return
	}
	// Skip printing if the object implements NoPrint and returns true
	fmt.Fprintln(out, result.Inspect())
	flushWriter(out)
}
